#ifndef CINPUT_H_
#define CINPUT_H_

#include "FBGeneral.h"

namespace FBShells
{
	enum e_FBKeys {
		FBKEY_NULL = 0,
		// mapping the keys in accordance to ASCII, for use in uchar arrays
		FBKEY_BACKSPACE = 8,
		FBKEY_TAB = 9,
		FBKEY_CLEAR = 12,
		FBKEY_RETURN = 13,
		FBKEY_PAUSE = 19,
		FBKEY_ESCAPE = 27,
		FBKEY_SPACE = 32,
		FBKEY_EXCLAMATION_MARK = 33,
		FBKEY_QUOTE = 34,
		FBKEY_HASH = 35,
		FBKEY_DOLLAR = 36,
		FBKEY_AMPERSAND = 38,
		FBKEY_APOSTROPHE = 39,
		FBKEY_LEFT_PARENTH = 40,
		FBKEY_RIGHTPARENTH = 41,
		FBKEY_ASTERISK = 42,
		FBKEY_PLUS = 43,
		FBKEY_COMMA	= 44,
		FBKEY_MINUS	= 45,
		FBKEY_PERIOD = 46,
		FBKEY_SLASH = 47,
		FBKEY_0	= 48,
		FBKEY_1	= 49,
		FBKEY_2	= 50,
		FBKEY_3	= 51,
		FBKEY_4	= 52,
		FBKEY_5	= 53,
		FBKEY_6	= 54,
		FBKEY_7	= 55,
		FBKEY_8	= 56,
		FBKEY_9 = 57,
		FBKEY_COLON = 58,
		FBKEY_SEMICOLON = 59,
		FBKEY_LEFT_ANGLE = 60,
		FBKEY_EQUALS = 61,
		FBKEY_RIGHT_ANGLE = 62,
		FBKEY_QUESTION_MARK = 63,
		FBKEY_AT = 64,
		FBKEY_A	= 65,
		FBKEY_B	= 66,
		FBKEY_C	= 67,
		FBKEY_D	= 68,
		FBKEY_E	= 69,
		FBKEY_F	= 70,
		FBKEY_G	= 71,
		FBKEY_H	= 72,
		FBKEY_I	= 73,
		FBKEY_J	= 74,
		FBKEY_K	= 75,
		FBKEY_L	= 76,
		FBKEY_M	= 77,
		FBKEY_N	= 78,
		FBKEY_O	= 79,
		FBKEY_P	= 80,
		FBKEY_Q	= 81,
		FBKEY_R	= 82,
		FBKEY_S	= 83,
		FBKEY_T	= 84,
		FBKEY_U	= 85,
		FBKEY_V	= 86,
		FBKEY_W	= 87,
		FBKEY_X	= 88,
		FBKEY_Y	= 89,
		FBKEY_Z	= 90,
		FBKEY_LEFT_BRACKET	= 91,
		FBKEY_BACKSLASH	= 92,
		FBKEY_RIGHT_BRACKET	= 93,
		FBKEY_CARET	= 94,
		FBKEY_UNDERSCORE = 95,
		FBKEY_BACKQUOTE	= 96,
		FBKEY_a	= 97,
		FBKEY_b	= 98,
		FBKEY_c	= 99,
		FBKEY_d	= 100,
		FBKEY_e	= 101,
		FBKEY_f	= 102,
		FBKEY_g	= 103,
		FBKEY_h	= 104,
		FBKEY_i	= 105,
		FBKEY_j	= 106,
		FBKEY_k	= 107,
		FBKEY_l	= 108,
		FBKEY_m	= 109,
		FBKEY_n	= 110,
		FBKEY_o	= 111,
		FBKEY_p	= 112,
		FBKEY_q	= 113,
		FBKEY_r	= 114,
		FBKEY_s	= 115,
		FBKEY_t	= 116,
		FBKEY_u	= 117,
		FBKEY_v	= 118,
		FBKEY_w	= 119,
		FBKEY_x	= 120,
		FBKEY_y	= 121,
		FBKEY_z	= 122,
		FBKEY_DELETE = 127,
		FBKEY_KEYPAD0 = 256,
		FBKEY_KEYPAD1 = 257,
		FBKEY_KEYPAD2 = 258,
		FBKEY_KEYPAD3 = 259,
		FBKEY_KEYPAD4 = 260,
		FBKEY_KEYPAD5 = 261,
		FBKEY_KEYPAD6 = 262,
		FBKEY_KEYPAD7 = 263,
		FBKEY_KEYPAD8 = 264,
		FBKEY_KEYPAD9 = 265,
		FBKEY_KEYPAD_PERIOD	= 266,
		FBKEY_KEYPAD_DIVIDE	= 267,
		FBKEY_KEYPAD_MULTIPLY = 268,
		FBKEY_KEYPAD_MINUS	= 269,
		FBKEY_KEYPAD_PLUS	= 270,
		FBKEY_KEYPAD_ENTER	= 271,
		FBKEY_KEYPAD_EQUALS	= 272,
		FBKEY_UP = 273,
		FBKEY_DOWN = 274,
		FBKEY_RIGHT = 275,
		FBKEY_LEFT = 276,
		FBKEY_INSERT = 277,
		FBKEY_HOME = 278,
		FBKEY_END = 279,
		FBKEY_PAGEUP = 280,
		FBKEY_PAGEDOWN = 281,
		FBKEY_F1 = 282,
		FBKEY_F2 = 283,
		FBKEY_F3 = 284,
		FBKEY_F4 = 285,
		FBKEY_F5 = 286,
		FBKEY_F6 = 287,
		FBKEY_F7 = 288,
		FBKEY_F8 = 289,
		FBKEY_F9 = 290,
		FBKEY_F10 = 291,
		FBKEY_F11 = 292,
		FBKEY_F12 = 293,
		FBKEY_F13 = 294,
		FBKEY_F14 = 295,
		FBKEY_F15 = 296,
		FBKEY_NUMLOCK = 300,
		FBKEY_CAPSLOCK = 301,
		FBKEY_SCROLL_LOCK = 302,
		FBKEY_RIGHT_SHIFT = 303,
		FBKEY_LEFT_SHIFT = 304,
		FBKEY_RIGHT_CTRL = 305,
		FBKEY_LEFT_CTRL	= 306,
		FBKEY_RIGHT_ALT	= 307,
		FBKEY_LEFT_ALT = 308,
		FBKEY_RIGHT_META = 309,
		FBKEY_LEFT_META	= 310,
		FBKEY_LEFT_SUPER = 311,		// Left super key (Or windows key)
		FBKEY_RIGHT_SUPER = 312,		// Right super key
		FBKEY_COMPOSE = 314		// Obsolete compose key
	};
	
	class CKeyboardHandler {
		protected:
			bool keys[512];
		public:
			virtual void Update() = 0;
			virtual bool IsKeyDown(e_FBKeys key) const = 0;
			virtual bool IsKeyUp(e_FBKeys key) const = 0;
			virtual bool GetKeyState(e_FBKeys key) const = 0;
	};

	class DummyKeyboard : public CKeyboardHandler {
		public:
			void Update() { }
			bool IsKeyDown(e_FBKeys key) { return false; }
			bool IsKeyUp(e_FBKeys key) { return true; }
			bool GetKeyState(e_FBKeys key) { return false; }
	};

	class CMouseHandler {
		public:
			virtual void Update() = 0;
			virtual bool IsButtonDown(unsigned int button) const = 0;
			virtual bool IsButtonUp(unsigned int button) const = 0;
			virtual bool GetButtonState(unsigned int button) const = 0;
			virtual const CPoint& GetMouseXY() const = 0;
	};

	class DummyMouse : public CMouseHandler {
		public:
			void Update() { }
			bool IsButtonDown(unsigned int button) const { return false; }
			bool IsButtonUp(unsigned int button) const { return true; }
			bool GetButtonState(unsigned int button) const { return false; }
			const CPoint& GetMouseXY() const { return CPoint(0,0); }
	};
	
	class CJoystickHandler {
		public:
			virtual void Update() = 0;
			virtual bool IsButtonDown(unsigned int joystick, unsigned int button) const = 0;
			virtual bool IsButtonUp(unsigned int joystick, unsigned int button) const = 0;
			virtual bool GetButtonState(unsigned int joystick, unsigned int button) const = 0;
			virtual const CPoint& GetOffset(unsigned int joystick, unsigned int pad) const = 0;
	};
	
	class DummyJoystick : public CJoystickHandler {
		void Update() { }
		bool IsButtonDown(unsigned int joystick, unsigned int button) const { return false; }
		bool IsButtonUp(unsigned int joystick, unsigned int button) const { return true; }
		bool GetButtonState(unsigned int joystick, unsigned int button) const { return false; }
		const CPoint& GetOffset(unsigned int joystick, unsigned int pad) const { return CPoint(0,0); }
	};
	
	class COtherInputHandler {
		public:
			virtual void Update() = 0;
	};

	class DummyOther : public COtherInputHandler {
		public:
			void Update() { }
	};

	class CInput
	{
		protected:
			CKeyboardHandler* m_keyboard;
			CMouseHandler* m_mouse;
			CJoystickHandler* m_joystick;
			COtherInputHandler* m_other;
		public:
			CInput() { }
			virtual ~CInput() {
				this->Shutdown();
			}
			
			virtual bool Initialize() = 0;
			virtual bool Shutdown() = 0;
			
			bool IsKeyDown(e_FBKeys key) const { return m_keyboard->IsKeyDown(key); }
			bool IsKeyUp(e_FBKeys key) const { return m_keyboard->IsKeyDown(key); }
			bool GetKeyState(e_FBKeys key) const { return m_keyboard->IsKeyDown(key); }
			
			const CPoint& GetMouseXY() const  { return m_mouse->GetMouseXY(); }
			bool IsMouseButtonDown(unsigned int button) const { return m_mouse->IsButtonDown(button); }
			bool IsMouseButtonUp(unsigned int button) const { return m_mouse->IsButtonUp(button); }
			bool GetMouseButtonState(unsigned int button) const { return m_mouse->GetButtonState(button); }

			bool IsJoystickButtonDown(unsigned int joystick, unsigned int button) const { return m_joystick->IsButtonDown(joystick, button); }
			bool IsJoystickButtonUp(unsigned int joystick, unsigned int button) const { return m_joystick->IsButtonUp(joystick, button); }
			bool GetJoystickButtonState(unsigned int joystick, unsigned int button) const { return m_joystick->GetButtonState(joystick, button); }
			const CPoint& GetJoystickOffset(unsigned int joystick, unsigned int pad) const { return m_joystick->GetOffset(joystick,pad); }
			
			void UpdateDevices() { m_keyboard->Update(); m_mouse->Update(); m_joystick->Update(); m_other->Update(); }
			
			/*******************************************/
			/* TODO: Add COtherInputHandler interface. */
			/*******************************************/
	};
	
	class DummyInput : public CInput {
		public:
			bool Initialize() { 
				m_keyboard = new DummyKeyboard;
				m_mouse = new DummyMouse;
				m_joystick = new DummyJoystick;
				m_other = new DummyOther;
				
				return true;
			}
			
			bool Shutdown() {
				delete m_keyboard;
				delete m_mouse;
				delete m_joystick;
				delete m_other;
				return true;
			}
	};
}

#endif /*CINPUT_H_*/
